﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//
using System.ComponentModel;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Globalization;

namespace Editor
{
    //
    // EditorConvertible
    //
    class StringConvert
    {
        private const char ValueSepartor = ';';

        public static String ToString(Object value)
        {
            if (value is IDK.Vector3)
            {
                var vec3 = (IDK.Vector3)value;
                return String.Format("{1}{0} {2}{0} {3}", ValueSepartor, vec3.x, vec3.y, vec3.z);
            }


            if (value is IDK.Quaternion)
            {
                var quat = (IDK.Quaternion)value;
                return String.Format("{1}{0} {2}{0} {3}{0} {4}", ValueSepartor, quat.x, quat.y, quat.z, quat.w);
            }

            return "Unknown Type";
        }

        public static Object FromString(Object value, String str)
        {
            String[] strArray = str.Split(new char[] { ValueSepartor });

            if (value is IDK.Vector3)
            {
                var vec3 = (IDK.Vector3)value;
                vec3.x = float.Parse(strArray[0]);
                vec3.y = float.Parse(strArray[1]);
                vec3.z = float.Parse(strArray[2]);
                return vec3;
            }


            if (value is IDK.Quaternion)
            {
                var quat = (IDK.Quaternion)value;
                quat.x = float.Parse(strArray[0]);
                quat.y = float.Parse(strArray[1]);
                quat.z = float.Parse(strArray[2]);
                quat.w = float.Parse(strArray[3]);
                return quat;
            }

            return value;
        }
    }


    /// <summary>
    /// FloatArrayConverter
    /// </summary>
    class FloatArrayConverter : ExpandableObjectConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context,
            Type sourceType)
        {
            if (sourceType == typeof(String))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }

        // Overrides the ConvertFrom method of TypeConverter.
        public override object ConvertFrom(ITypeDescriptorContext context,
           CultureInfo culture, object value)
        {
            if (value is String)
            {
                var obj = context.PropertyDescriptor.GetValue(context.Instance);

                obj = StringConvert.FromString(obj, value as String);

                context.PropertyDescriptor.SetValue(context.Instance, obj);
                return obj;
            }
            return base.ConvertFrom(context, culture, value);
        }
        // Overrides the ConvertTo method of TypeConverter.
        public override object ConvertTo(ITypeDescriptorContext context,
           CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(String))
            {
                var obj = context.PropertyDescriptor.GetValue(context.Instance);
                return StringConvert.ToString(obj);
            }
            else
            {
                return base.ConvertTo(context, culture, value, destinationType);
            }
        }
    }

    public class CustomPropertyDescriptor : PropertyDescriptor
    {
        private TypeConverter mTypeConverter;

        public CustomPropertyDescriptor(MemberDescriptor descr)
            : base(descr)
        {
            Implementation = descr as PropertyDescriptor;
        }

        public CustomPropertyDescriptor(MemberDescriptor descr, TypeConverter typeConverter)
            : base(descr)
        {
            Implementation = descr as PropertyDescriptor;
            mTypeConverter = typeConverter;
        }

        public CustomPropertyDescriptor(MemberDescriptor descr, Attribute[] attrs)
            : base(descr, attrs)
        {
        }

        public CustomPropertyDescriptor(string name, Attribute[] attrs)
            : base(name, attrs)
        {
        }

        public PropertyDescriptor Implementation
        {
            get;
            set;
        }

        public override TypeConverter Converter
        {
            get
            {
                return mTypeConverter;
            }
        }

        public override bool CanResetValue(object component)
        {
            return Implementation.CanResetValue(component);
        }

        public override Type ComponentType
        {
            get { return Implementation.ComponentType; }
        }

        public override object GetValue(object component)
        {
            return Implementation.GetValue(component);
        }

        public override bool IsReadOnly
        {
            get { return Implementation.IsReadOnly; }
        }

        public override Type PropertyType
        {
            get { return Implementation.PropertyType; }
        }

        public override void ResetValue(object component)
        {
            Implementation.ResetValue(component);
        }

        public override void SetValue(object component, object value)
        {
            Implementation.SetValue(component, value);
        }

        public override bool ShouldSerializeValue(object component)
        {
            return Implementation.ShouldSerializeValue(component);
        }
    }

    public class EditorConverter : ExpandableObjectConverter
    {

        /*
        static public Dictionary<Type, Type> ConverterTypeToIDK = new Dictionary<Type, Type>()
        {

           typeof(IDK.Vector3), typeof(ExpandableObjectConverter)
        };*/

        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var baseProperties = base.GetProperties(context, value, attributes);

            List<PropertyDescriptor> properties = new List<PropertyDescriptor>();

            foreach (PropertyDescriptor desc in baseProperties)
            {
                if (desc.Name == "Name")
                {
                    continue; // ignore linked variable
                }

                if (desc.PropertyType == typeof(IDK.Vector3))
                {
                    properties.Add(new CustomPropertyDescriptor(desc, new FloatArrayConverter()));
                }
                else
                {
                    var typeCode = Type.GetTypeCode(desc.PropertyType);
                    if (typeCode == TypeCode.Object)
                    {
                        properties.Add(new CustomPropertyDescriptor(desc, new EditorConverter()));
                    }
                    else
                    {
                        properties.Add(desc);
                    }
                }
            }
            return new PropertyDescriptorCollection(properties.ToArray());
        }
    }

    public class GameObjectInfoConverter : EditorConverter
{
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        private static bool IsBaseType(Type instance, Type baseType)
        {
            if (instance == null)
            {
                return false;
            }

            if (instance.Equals(baseType))
            {
                return true;
            }
            else
            {
                return IsBaseType(instance.BaseType, baseType);
            }
        }

        public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            List<Type> items = new List<Type>();
            
            Type baseType = typeof(Game.GameObjectInfo);
            System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(Game.GameScene));

            foreach (Type it in assembly.GetTypes())
            {
                if (IsBaseType(it, baseType) && !it.IsAbstract)
                {
                    items.Add(it);
                }
            }

            return new StandardValuesCollection(items);
        }

        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(String))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }

        // Overrides the ConvertFrom method of TypeConverter.
        public override object ConvertFrom(ITypeDescriptorContext context,
           CultureInfo culture, object value)
        {
            if (value is String)
            {
                System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(Game.GameScene));
                Type type = assembly.GetType(value as String);
                if (type != null)
                {
                    var instance = System.Activator.CreateInstance(type) as Game.GameObjectInfo;
                    //context.PropertyDescriptor.SetValue(context.Instance, instance);
                    return instance;
                }
            }
            return base.ConvertFrom(context, culture, value);
        }

        // Overrides the ConvertTo method of TypeConverter.
        public override object ConvertTo(ITypeDescriptorContext context,
           CultureInfo culture, object value, Type destinationType)
        {

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

    public class EditorSceneObject
    {
        public IDK.SceneNode SceneNode;

        public EditorSceneObject(IDK.SceneNode sceneNode)
        {
            SceneNode = sceneNode;
        }

        public String Name
        {
            get { return SceneNode.GetName(); }
        }

        [TypeConverter(typeof(FloatArrayConverter))]
        public IDK.Vector3 Position
        {
            get
            {
                return SceneNode.GetTransformation().GetLocalPosition();
            }
            set
            {
                SceneNode.GetTransformation().SetLocalPosition(value);
            }
        }
    }

    public class EditorSceneGameObject : EditorSceneObject
    {
        private Game.GameObjectInfo mGameObjectInfo;
        private Game.WorldScene mWorldScene;

        public EditorSceneGameObject(IDK.SceneNode sceneNode, Game.WorldScene scene)
            : base(sceneNode)
        {
            mWorldScene = scene;
        }


        protected void CopySameData(Object src, Object dst)
        {
            var dstType = dst.GetType();
            foreach (System.Reflection.PropertyInfo it in src.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public))
            {
                var property = dstType.GetProperty(it.Name);
                if (property != null)
                {
                    property.SetValue(dst, it.GetValue(src, null), null);
                }
            }
        }

        [TypeConverter(typeof(GameObjectInfoConverter))]
        public Game.GameObjectInfo GameObjectInfo
        {
            get
            {
                return mGameObjectInfo;
            }
            set
            {
                // update name 
                value.Name = Name;

                // update manager
                var manager = mWorldScene.GetManager<Game.GameObjectManager>();

                if (mGameObjectInfo != null)
                {
                    // Copy same data
                    CopySameData(mGameObjectInfo, value);

                    // remove old game object
                    if (manager.HasGameObject(Name))
                    {
                        manager.DeleteGameObject(Name, mWorldScene);
                    }
                }

                //making new object info
                if (!manager.HasGameObject(Name))
                {
                    //update game object info
                    manager.GameObjectsType[Name] = value;

                    manager.CreateGameObject(Name, mWorldScene);
                }

                mGameObjectInfo = value;
            }
        }
    }

    class GroupEditorSceneObject : List<EditorSceneObject>
    {

    }

    class EditorScene : Game.GameScene
    {
        public EditorScene(IDK.Engine engine)
            : base(engine)
        {
            Pause = true;
        }

        public void SwitchGameMode(bool mode)
        {
            ActionManager.InputSubscriber.Enable(mode);
        }
    }
}